package com.business.activiti.controller;

import cn.hutool.core.map.MapUtil;
import com.business.activiti.model.auto.AppealInfo;
import com.business.activiti.model.auto.TaskNode;
import com.business.activiti.model.auto.TaskNodeExample;
import com.business.activiti.service.ActivitiService;
import com.business.activiti.service.AppealInfoService;
import com.business.activiti.service.ModelService;
import com.business.activiti.service.TaskNodeService;
import com.business.form.model.auto.FormInfo;
import com.business.form.model.auto.FormInfoExample;
import com.business.form.model.auto.FormInstanceFieldValue;
import com.business.form.model.auto.FormInstanceFieldValueExample;
import com.business.form.service.FormInfoService;
import com.business.form.service.FormInstanceFieldValueService;
import com.business.system.common.base.BaseController;
import com.business.system.common.base.PageInfo;
import com.business.system.common.domain.AjaxResult;
import com.business.system.common.log.Log;
import com.business.system.model.auto.TsysRole;
import com.business.system.model.auto.TsysUser;
import com.business.system.model.custom.TableSplitResult;
import com.business.system.model.custom.Tablepar;
import com.business.system.model.custom.TitleVo;
import com.business.system.service.SysUserService;
import com.business.system.shiro.util.ShiroUtils;
import com.business.system.util.StringUtils;
import com.business.util.CommUtil;
import com.business.util.DateUtil;
import com.business.util.ListUtils;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.gson.Gson;
import org.activiti.bpmn.model.*;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.engine.*;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.repository.*;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.*;
import java.lang.reflect.Field;
import java.util.*;

@Controller
@RequestMapping(value = "/act")
public class ActivitiController extends BaseController {
    @Autowired
    private ActivitiService activitiService;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private IdentityService identityService;

    @Autowired
    private HistoryService historyService;

    @Autowired
    private SysUserService userService;


    @Autowired
    private TaskService taskService;

    @Autowired
    private FormInfoService formInfoService;

    @Autowired
    ProcessEngine processEngine;

    @Autowired
    ModelService modelService;

    @Autowired
    private FormInstanceFieldValueService formInstanceFieldValueService;

    @Autowired
    private AppealInfoService appealInfoService;

    @Autowired
    private TaskNodeService taskNodeService;

    private String prefix = "activiti";

    @RequestMapping("/view")
    public String view(ModelMap model){
        String str="流程";
        setTitle(model, new TitleVo("列表", str+"管理", true,"欢迎进入"+str+"页面", true, false));
        return prefix + "/list";
    }

    @RequestMapping("/flowElementsView")
    public String flowElementsView(ModelMap model, HttpServletRequest req){
        String str="流程";
        setTitle(model, new TitleVo("列表", str+"管理", true,"欢迎进入"+str+"页面", true, false));
        String processDefinitionId=req.getParameter("processDefinitionId");
        req.setAttribute("processDefinitionId",processDefinitionId);
        return prefix + "/flowElementsViewList";
    }

    /**
     * 模型列表 json
     */
    @RequestMapping(value = "modelList")
    @ResponseBody
    public void modelList(Tablepar tablepar, String searchTxt, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        ModelQuery modelQuery = repositoryService.createModelQuery();
        List<Model> modelList = new ArrayList<Model>();
        if (!StringUtils.isEmpty(searchTxt)) {
            modelQuery.modelNameLike("%" + searchTxt + "%");
        }
        modelList=modelQuery.list();
        Integer pageNum=Integer.parseInt(req.getParameter("pageNum"));
        Integer pageSize=Integer.parseInt(req.getParameter("pageSize"));

        int total=modelList==null?0:modelList.size();
        List<Model> list= (List<Model>) ListUtils.getSubList(modelList,pageNum,pageSize);
        TableSplitResult<Model> result=new TableSplitResult<Model>(pageNum, (long)total, list);
        String json= new Gson().toJson(result);
        resp.getWriter().write(json);
    }

    /**
     * 部署列表 json
     */
    @Log(title = "获取流程定义列表")
    @RequestMapping(value = "list")
    @ResponseBody
    public void list(Tablepar tablepar, String searchTxt, HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //runtimeService.startProcessInstanceById("app_process:1:27507");
        //taskService.complete("");

        ProcessDefinitionQuery processDefinitionQuery = repositoryService
                .createProcessDefinitionQuery();
        List<ProcessDefinition> processDefinitionList = new ArrayList<ProcessDefinition>();
        if (!StringUtils.isEmpty(searchTxt)) {
            processDefinitionQuery.processDefinitionNameLike("%" + searchTxt + "%");
        }
        processDefinitionList=processDefinitionQuery.orderByProcessDefinitionVersion().desc().list();
        List<ProcessDefinition> plist=new ArrayList<ProcessDefinition>();
        if(processDefinitionList!=null){
            Map<String,Integer> keyMap=new HashMap<String,Integer>();
            for(ProcessDefinition p:processDefinitionList){
                Deployment deployment= repositoryService.createDeploymentQuery().deploymentId(p.getDeploymentId()).singleResult();
                if(keyMap.get(deployment.getKey())==null||keyMap.get(deployment.getKey())<p.getVersion()){
                    keyMap.put(deployment.getKey(),p.getVersion());
                    plist.add(p);
                }
            }
        }
        processDefinitionList=plist;

        PageInfo<ProcessDefinition> page = new PageInfo<ProcessDefinition>(processDefinitionList);
        /******************/




        /******************/
        Integer pageNum=Integer.parseInt(req.getParameter("pageNum"));
        Integer pageSize=Integer.parseInt(req.getParameter("pageSize"));

        int total=processDefinitionList==null?0:processDefinitionList.size();
        List<ProcessDefinition> list= (List<ProcessDefinition>) ListUtils.getSubList(processDefinitionList,pageNum,pageSize);
        List<Map> mapList=new ArrayList<Map>();
        if(list!=null){
            /*runtimeService.startProcessInstanceById("process:1:10004");
            runtimeService.startProcessInstanceById("process:2:20007");*/
            //repositoryService.suspendProcessDefinitionById("process:1:10004");
            /*repositoryService.activateProcessDefinitionById("process:1:10004");
            repositoryService.activateProcessDefinitionById("process:2:20007");*/
            for(ProcessDefinition pd:list){
                Deployment dep=repositoryService.createDeploymentQuery().deploymentId(pd.getDeploymentId()).singleResult();
                Map map=new HashMap<String,String>();
                map.put("key",pd.getKey());
                map.put("resourceName",pd.getResourceName());
                map.put("diagramResourceName",pd.getDiagramResourceName());
                map.put("name",dep.getName());
                map.put("deploymentTime",dep.getDeploymentTime().toString());
                map.put("processDefinitionId",pd.getId());
                map.put("deploymentId",pd.getDeploymentId());
                map.put("isSuspended",pd.isSuspended());
                map.put("version",pd.getVersion());
                map.put("modelId",dep.getKey());
                mapList.add(map);
            }
        }
        TableSplitResult<Map> result=new TableSplitResult<Map>(pageNum, (long)total, mapList);
        String json= new Gson().toJson(result);
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().write(json);


        //*****************

        //*****************
    }

    /**
     * 根据ProcessDefinitionId获取流程定义中的节点列表
     */
    @Log(title = "根据ProcessDefinitionId获取流程定义中的节点列表")
    @RequestMapping(value = "flowElementsList")
    @ResponseBody
    public void getProcessDefinitionFlowElements(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String processDefinitionId=req.getParameter("processDefinitionId");
        BpmnModel model = repositoryService.getBpmnModel(processDefinitionId);
        List<Map> mapList=new ArrayList<Map>();
        if(model != null) {
            Collection<FlowElement> flowElements = model.getMainProcess().getFlowElements();
            for(FlowElement e : flowElements) {
                System.out.println("flowelement id:" + e.getId() + "  name:" + e.getName() + "   class:" + e.getClass().toString());
                Map map=new HashMap<String,String>();
                map.put("id",e.getId());
                map.put("name",e.getName());
                String elementClass=e.getClass().toString().substring(e.getClass().toString().lastIndexOf(".")+1,e.getClass().toString().length());
                map.put("elementClass",elementClass);
                if("StartEvent".equals(elementClass)){
                    map.put("elementClassDescript","开始按钮");
                }else if("UserTask".equals(elementClass)){
                    map.put("elementClassDescript","用户任务");
                    UserTask t=(UserTask)e;
                    List<CustomProperty> customProperties = new ArrayList<>();
                    Map<String, String> map1 = new HashMap<String,String>();
                    for (String k : map1.keySet()) {
                        CustomProperty customProperty = new CustomProperty();
                        customProperty.setName(k);
                        customProperty.setSimpleValue(map1.get(k));
                        customProperty.setId(k);
                        customProperties.add(customProperty);
                    }
                    t.setCustomProperties(customProperties);
                    //ExtensionElement EE
                    Map m=t.getExtensionElements();
                    System.out.println(m.get("form_id_1"));

                }else if("SequenceFlow".equals(elementClass)){
                    map.put("elementClassDescript","连线");
                }else if("EndEvent".equals(elementClass)){
                    map.put("elementClassDescript","结束");
                }
                mapList.add(map);
            }
        }
        TableSplitResult<Map> result=new TableSplitResult<Map>(1, (long)mapList.size(), mapList);
        String json= new Gson().toJson(result);
        resp.setCharacterEncoding("utf-8");
        resp.getWriter().write(json);
    }

    /**
     * 修改流程定义状态
     * 如果当前为挂起状态，变更为激活
     * 如果当前为激活状态 变更为挂起
     * @return
     */
    @Log(title = "修改流程定义状态")
    @RequestMapping(value = "changeProcessDefinitionSuspendStatus")
    @ResponseBody
    public AjaxResult changeProcessDefinitionSuspendStatus(HttpServletRequest req, HttpServletResponse resp){
        String processDefinitionId=req.getParameter("processDefinitionId");
        ProcessDefinition pd=repositoryService.getProcessDefinition(processDefinitionId);
        Boolean isSuccess=false;
        if(pd!=null){
            try {
                if(pd.isSuspended()){
                    //当前为挂起状态，变更为激活
                    repositoryService.activateProcessDefinitionById(processDefinitionId);
                }else{
                    //当前为激活状态 变更为挂起
                    repositoryService.suspendProcessDefinitionById(processDefinitionId);
                }
                isSuccess=true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        if(isSuccess){
            return AjaxResult.success();
        }else{
            return AjaxResult.error();
        }
    }

    /**
     * 新增一个流程模型，返回该模型json
     * @return
     */
    @Log(title = "新增一个流程模型")
    @RequestMapping(value = "addNewModel")
    @ResponseBody
    public AjaxResult addNewModel(){
        Model model=activitiService.addNewModel();
        return AjaxResult.success().put("model",model);
    }

    @Log(title = "流程模型发布为流程")
    @RequestMapping("modelDeploy/{modelId}")
    @ResponseBody
    public AjaxResult modelDeploy(@PathVariable String modelId){
        Deployment deployment=activitiService.modelDeploy(modelId);

        return AjaxResult.success().put("deployment",deployment);
    }

    //根据流程定义id查看流程图
    @Log(title = "查看流程图")
    @RequestMapping("/preProcessImage")
    public void preProcessImage(HttpServletResponse response, @RequestParam String processDefinitionId)throws Exception {
        ProcessDefinition pd=repositoryService.getProcessDefinition(processDefinitionId);
        InputStream imageStream=null;
        if(pd==null){
            throw new Exception("无法找到流程");
        }
        imageStream = repositoryService.getResourceAsStream(pd.getDeploymentId(),pd.getDiagramResourceName());
        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        imageStream.close();
    }

    @RequestMapping("/getUserList")
    @ResponseBody
    public Object getUserList(Tablepar tablepar,String searchTxt){
        com.github.pagehelper.PageInfo<TsysUser> page=sysUserService.list(tablepar,searchTxt) ;
        TableSplitResult<TsysUser> result=new TableSplitResult<TsysUser>(page.getPageNum(), page.getTotal(), page.getList());
        return  result;
    }

    /**
     * 角色列表  （用于模型编辑器角色选择）
     * @param tablepar
     * @param searchTxt 搜索字符
     * @return
     */
    @RequestMapping("/getGroupList")
    @ResponseBody
    public Object getGroupList(Tablepar tablepar,String searchTxt){
        com.github.pagehelper.PageInfo<TsysRole> page=sysRoleService.list(tablepar,searchTxt) ;
        TableSplitResult<TsysRole> result=new TableSplitResult<TsysRole>(page.getPageNum(), page.getTotal(), page.getList());
        return  result;
    }

    /**
     * 获取表单定义列表（用于模型编辑器表单选择）
     * @param tablepar
     * @param searchTxt
     * @return
     */
    @RequestMapping("/getFormList")
    @ResponseBody
    public Object list(Tablepar tablepar, String searchTxt){
        com.github.pagehelper.PageInfo<FormInfo> page=formInfoService.list(tablepar,searchTxt) ;
        TableSplitResult<FormInfo> result=new TableSplitResult<FormInfo>(page.getPageNum(), page.getTotal(), page.getList());
        return  result;
    }


    /**
     * 启动一个流程实例 仅测试用
     */
    @RequestMapping("/startProcess")
    @ResponseBody
    public AjaxResult startProcessById(HttpServletRequest request){
        String processDefinisionId=request.getParameter("processDefinisionId");
        ProcessInstance processInstance=runtimeService.startProcessInstanceById(processDefinisionId);
        return new AjaxResult().success();
    }


    /**
     * 根据任务id完成任务
     * @param request
     * @return
     */
    @RequestMapping("/completeTask")
    @ResponseBody
    public AjaxResult completeTask(HttpServletRequest request){
        Enumeration enu=request.getParameterNames();
        HashMap<String,String> map=new HashMap<String,String>();
        while(enu.hasMoreElements()){
            String paraName=(String)enu.nextElement();
            map.put(paraName,request.getParameter(paraName));
        }
        TsysUser user=ShiroUtils.getUser();
        String taskId=map.get("taskId");
        activitiService.completeTaskById(taskId,map,user);
        return new AjaxResult().success();
    }

    /**
     * 根据当前登录用户获取待处理任务列表 测试用
     */
    @RequestMapping("/getTaskListByCurrentUser")
    @ResponseBody
    public AjaxResult getTaskListByCurrentUser(){
        TsysUser user= ShiroUtils.getUser();
        List<Task> tasks=activitiService.getTaskListByUser(user);
        List<Object> objects=new ArrayList<Object>();
        if(tasks!=null){
            for(Task task:tasks){
                objects.add(task);
            }
        }
        String[] ps = { "id", "name","description","formKey","processInstanceId","createTime" };
        List<Map<String, Object>> list= CommUtil.list2MapList(objects,ps);
        return new AjaxResult().success().put("tasks",list);
    }


    /**
     * 获取流程实例的已完成任务列表 包含开始节点
     * @param request
     */
    @RequestMapping("/getTaskNodesByProcessInstance")
    @ResponseBody
    public Object getTaskNodesByProcessInstance(HttpServletRequest request){
        String processInstanceId=request.getParameter("processInstanceId");
        TaskNodeExample example=new TaskNodeExample();
        example.createCriteria().andProcessInstanceIdEqualTo(processInstanceId);
        List<TaskNode> taskNodes=taskNodeService.selectByExample(example);
        List<Map<String, Object>> mapList=new ArrayList<Map<String, Object>>();
        AppealInfo appealInfo=appealInfoService.selectOneByProcessInstance(processInstanceId);
        if(appealInfo!=null){
            Map<String,Object> startMap=new HashMap<String,Object>();
            startMap.put("name","开始");
            startMap.put("createTime",DateUtil.date2str(appealInfo.getCreateTime()));
            startMap.put("endTime",DateUtil.date2str(appealInfo.getCreateTime()));
            startMap.put("formInstanceId",appealInfo.getStartEventFormId());
            mapList.add(0,startMap);
        }
        for(TaskNode taskNode:taskNodes){
            Map<String, Object> map=new HashMap<String, Object>();
            HistoricTaskInstance task=historyService.createHistoricTaskInstanceQuery().taskId(taskNode.getTaskId()).singleResult();
            TsysUser user=userService.selectByPrimaryKey(taskNode.getUserId());
            map.put("name",task.getName());
            map.put("createTime", DateUtil.date2str(task.getCreateTime()));
            map.put("endTime",DateUtil.date2str(task.getEndTime()));
            map.put("formInstanceId",taskNode.getFormInstanceId());
            map.put("userName",user==null?"":user.getNickname());
            mapList.add(map);
        }
        TableSplitResult<Map<String, Object>> result=new TableSplitResult<Map<String, Object>>(1, (long)mapList.size(),mapList);
        return result;
    }

    /**
     * 获取流程实例的已存在任务列表
     * @param request
     */
    @RequestMapping("/getTasksByProcessInstance")
    @ResponseBody
    public Object getTasksByProcessInstance(HttpServletRequest request){
        String processInstanceId=request.getParameter("processInstanceId");
        List<HistoricTaskInstance> tasks=historyService.createHistoricTaskInstanceQuery().processInstanceId(processInstanceId).list();
        List<Object> objects=new ArrayList<Object>();

        if(tasks!=null){
            for(HistoricTaskInstance task:tasks){
                objects.add(task);
            }
        }
        String[] ps = { "id", "name","description","formKey","processInstanceId","createTime","endTime" };
        List<Map<String, Object>> list= CommUtil.list2MapList(objects,ps);
        //添加开始节点499090815935713280
        TableSplitResult<Map<String, Object>> result=new TableSplitResult<Map<String, Object>>(1, (long)list.size(),list);
        return result;
    }

    /**
     * 流程历史节点高亮显示
     * @param processInstanceId
     * @param response
     * @throws Exception
     */
    @RequestMapping("/getProccessActiveNodeImage")
    public void getProccessActiveNodeImage(String processInstanceId, HttpServletResponse response) throws Exception {
        InputStream imageStream =modelService.generateProcessDiagram(processInstanceId);
        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        imageStream.close();
    }

    /**
     * 流程图当前节点高亮显示
     * @param processInstanceId
     * @param response
     * @throws Exception
     */
    @RequestMapping("/getProccessCurrentNodeImage")
    public void getProccessCurrentNodeImage(String processInstanceId, HttpServletResponse response) throws Exception {
        InputStream imageStream =modelService.generateImage(processInstanceId);
        response.setContentType("image/png");
        OutputStream os = response.getOutputStream();
        int bytesRead = 0;
        byte[] buffer = new byte[8192];
        while ((bytesRead = imageStream.read(buffer, 0, 8192)) != -1) {
            os.write(buffer, 0, bytesRead);
        }
        os.close();
        imageStream.close();
    }

    /**
     * 根据表单实例id获取表单的各个字段的值
     * @param request
     * @return
     */
    @RequestMapping("/getFormValues")
    @ResponseBody
    public AjaxResult getFormValues(HttpServletRequest request){
        String formInstanceId=request.getParameter("formInstanceId");
        FormInstanceFieldValueExample example=new FormInstanceFieldValueExample();
        example.createCriteria().andFormInstanceIdEqualTo(formInstanceId);
        List<FormInstanceFieldValue> values=formInstanceFieldValueService.selectByExample(example);
        return AjaxResult.success().put("list",values);
    }


    /**
     * 获取模型列表 (建议用于诉求类型中模型key的下拉框渲染)
     * @return
     */
    @RequestMapping("/getModelKeyList")
    @ResponseBody
    public AjaxResult getModelKeyList(){
        List<Model> models=repositoryService.createModelQuery().list();
        List<Object> objects=new ArrayList<Object>();
        if(models!=null){
            for(Model model:models){
                objects.add(model);
            }
        }
        String[] ps = { "key", "name"};
        List<Map<String, Object>> list= CommUtil.list2MapList(objects,ps);
        return AjaxResult.success().put("models",list);
    }
}
